/*
 * BlockStorageStrategy.cpp
 *
 *  Created on: 03/10/2009
 *      Author: nik0l4z
 */

#include "../Strategies/Storage/BlockStorageStrategy.h"
#include "ArchivoTexto.h"
#include "ArchivoBinario.h"
#include "../Strategies/Storage/TablaEspacioLibre.h"
#include "Registro.h"




BlockStorageStrategy::BlockStorageStrategy() {

//harcodeado el tamanio de bloque
	this->tablaEspacioLibre = new TablaEspacioLibre("TestBlockStorageStrategy.txt", 100);

}

BlockStorageStrategy::BlockStorageStrategy(const string& nombrearchivo, int tamanioBloque) {

//harcodeado el tamanio de bloque
	this->tablaEspacioLibre = new TablaEspacioLibre(nombrearchivo, tamanioBloque);

	this->tamanioBloque=tamanioBloque;

	this->nombreArchivo = nombrearchivo;

	this->tamanioCritico = 0.8 * tamanioBloque;

}

/**
 * tamanioCritico representa el porcentaje que se puede llenar el bloque en una incersion
 */
BlockStorageStrategy::BlockStorageStrategy(const string& nombreArchivoEspacioLibre, int tamanioBloque, int porcentajeLimite) {

	this->tablaEspacioLibre = new TablaEspacioLibre(nombreArchivoEspacioLibre, tamanioBloque);

	this->tamanioBloque=tamanioBloque;

	this->tamanioCritico = (porcentajeLimite * tamanioBloque)/100;


}

BlockStorageStrategy::~BlockStorageStrategy() {
	//this->tablaEspacioLibre->SerializarTabla();
	delete this->tablaEspacioLibre;
	this->tablaEspacioLibre= NULL;

	//Este delete no debe hacerce desde aca sino desde el ambito en donde se reservo la memoria
	//if (this->almacenamiento!=NULL) delete this->almacenamiento;

}


bool BlockStorageStrategy::buscarEnBuffer (Bloque* bloque, int nroBloque) {
	return false;
}

void BlockStorageStrategy::deleteDataBuffer(Clave* clave, int filePos) {

}

list<Registro>::iterator* BlockStorageStrategy::buscarRegistro(Bloque* bloque, Clave* clave) {
	bool encontrada = false;
	list<Registro>::iterator* piterador =NULL;
	list<Registro>::iterator it=bloque->getRegistros()->begin();
	while((it!=bloque->getRegistros()->end()) && (!encontrada)) {

		if(clave->Comparar(it->getClave())==0) {
			encontrada = true;
			piterador = &it;
			return piterador;
		}

		else
		 it++;
	}

	return piterador;

}

int BlockStorageStrategy::saveData(Dato *data){

	Clave clave("campoClave",data);

	Registro nuevoReg(data->serializar(),clave);

	bool encontrado = false;
	int nroBloque = 0;
	int tamanioOcupado;
	bool estaEnBuffer = false;
	Bloque* bloque = new Bloque(tamanioBloque);

	while ((tablaEspacioLibre->GetEspacioLibre(nroBloque)!=-1) && (!encontrado)) {

		tamanioOcupado =tamanioBloque-tablaEspacioLibre->GetEspacioLibre(nroBloque);
		if (tamanioOcupado + nuevoReg.getLongitudDato() <= tamanioCritico) {

			estaEnBuffer=buscarEnBuffer(bloque,nroBloque);
			if(estaEnBuffer) {
				bloque->getRegistros()->push_back(nuevoReg);
			}

			else {
				bloque = this->LeerBloque(nroBloque);
				bloque->getRegistros()->push_back(nuevoReg);
			}

			encontrado = true;
		}

		else
			nroBloque++;

	}

	if(!encontrado) {
		bloque->setNroBloque(nroBloque);
		bloque->getRegistros()->push_back(nuevoReg);
		//cout<<nroBloque<<endl;
		//cout<<bloque->getRegistros()->size()<<endl;
	}

	this->GrabarBloque(*bloque);


    return nroBloque;
}

void BlockStorageStrategy::deleteData(Clave* clave, int filePos) {

	Bloque* bloque;
	deleteDataBuffer(clave,filePos);
	bloque=this->LeerBloque(filePos);
	if(buscarRegistro (bloque, clave)!=NULL) {
		bloque->getRegistros()->erase(*buscarRegistro (bloque, clave));
		this->GrabarBloque(*bloque);
	}

}

int BlockStorageStrategy::updateData(Dato *data,int filePos){
	Clave* clave = new Clave("campoClave",data);

	Registro nuevoReg(data->serializar(),*clave);

	//bool encontrado = false;
	//int nroBloque = 0;
	int desborde;
	bool estaEnBuffer = false;
	Bloque* bloque;
	desborde=tablaEspacioLibre->GetEspacioLibre(filePos)-nuevoReg.getLongitudDato();
	estaEnBuffer = buscarEnBuffer(bloque, filePos);
	if(estaEnBuffer) {
		if(desborde<0) {
			this->saveData(data);
			this->deleteData(clave, filePos);
			this->GrabarBloque(*bloque);
		}

		else if(buscarRegistro (bloque, clave)!=NULL) {
			bloque->getRegistros()->erase(*buscarRegistro (bloque, clave));
			bloque->getRegistros()->push_back(nuevoReg);
			this->GrabarBloque(*bloque);
		}
	}

	else {
		bloque = this->LeerBloque(filePos);
		if(buscarRegistro(bloque,clave)!= NULL) {
			if(desborde<0) {
				this->deleteData(clave, filePos);
				this->saveData(data);
				cout<<"se desbordo"<<endl;
			}

			else {
				bloque->getRegistros()->erase(*buscarRegistro (bloque, clave));
				bloque->getRegistros()->push_back(nuevoReg);
				this->GrabarBloque(*bloque);
			}
		}
		else cout<< "no se encontro el dato que se deseaba modificar"<< endl;
	}
	return bloque->getNroBloque();
}

void BlockStorageStrategy::Compactar() {

	int i =0;
	int cantidadBloques;
	Bloque* bloque;
	//ArchivoBinario arch(nombreArchivo,tamanioBloque);

	while(tablaEspacioLibre->GetEspacioLibre(i)!=-1) {
		if(tablaEspacioLibre->GetEspacioLibre(i)==0) {
			cantidadBloques=almacenamiento->GetTamanio()/tamanioBloque;
			bloque = LeerBloque(cantidadBloques-1);
			bloque->setNroBloque(i);
			GrabarBloque(*bloque);
			//truncarArchivo();

		}

		i++;
	}

}

Almacenamiento *BlockStorageStrategy::getStorage() const{
	return this->almacenamiento;
}

void BlockStorageStrategy::setStorage(Almacenamiento *storage) {
	this->almacenamiento = storage;
}


string BlockStorageStrategy::PrepararBloque(Bloque bloque) {







	string bloqueSerializado;

	/*-----------------------------------------------------------*/
	//Actualizar espacios libres
	this->tablaEspacioLibre->SetEspacioLibre(bloque.getNroBloque(), bloque.getEspaciolibre());
	 cout<<"actualizando espacio libre"<<endl;

	 this->tablaEspacioLibre->SerializarTabla();

	 cout<<"tbla serializada"<<endl;
	return bloqueSerializado;

}

void BlockStorageStrategy::GrabarBloque(Bloque bloque){
	//cout << "no se hace nada en GrabarBloque usar ahora PrepararBloque(Bloque bloque)" <<endl;
	string bloqueSerializado;
 	bloqueSerializado= bloque.Serializar();

 	this->resourceStrategy->Save(bloqueSerializado,bloque.getNroBloque());

 	//Actualizar espacios libres
 	this->tablaEspacioLibre->SetEspacioLibre(bloque.getNroBloque(), bloque.getEspaciolibre());

 	this->tablaEspacioLibre->SerializarTabla();

 }

Bloque* BlockStorageStrategy::LeerBloque(int pos)
{
	Bloque* bloque;
	bloque = new Bloque(pos,tamanioBloque);
	int espacioLibre;
	espacioLibre = this->tablaEspacioLibre->GetEspacioLibre(bloque->getNroBloque());
	bloque->setEspacioLibre(espacioLibre);

	string pbloqueSerializado;

	/*if((this->getStorage())->getData(pos)=="sigue")
		cout<<"es nulo"<<endl;
	else*/
	//pbloqueSerializado=this->getStorage()->getData(pos); //Se comenta linea, para que ahora la lectura se haga mediante Estrategia recurso
	
	pbloqueSerializado=this->resourceStrategy->GetData(pos);

	bloque->desSerializar(pbloqueSerializado);


	return bloque;
}

string BlockStorageStrategy::getData(Clave *clave, int fileReference){

	Bloque *bloque;

	bloque = this->LeerBloque(fileReference);

	list <Registro> *registros;


	registros = bloque->getRegistros();

	list <Registro>::iterator it;

    int resultado;


    for( it = registros->begin(); it != registros->end(); ++it )
    {


       	resultado=clave->Comparar(it->getClave());

    	if (resultado==0){

    		string datoSerializado = it->getDato();

    		return datoSerializado;

    	}


     }

    return "";

}
